/*
 * Copyright (c) 2022. China Mobile (SuZhou) Software Technology Co.,Ltd. All rights reserved.
 * Lakehouse is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *          http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 */

import { executeSql, formatSql } from '@/service/modules/sqlconsole'
import {
  SqlExecResult,
  SqlQueryJobItemDtos
} from '@/service/modules/sqlconsole/types'
import { useSqlExecTabStore } from '@/store/sqlconsole/sql-exec-tab'
import { SqlExec } from '@/store/sqlconsole/types'
import { ExclamationCircleFilled } from '@vicons/antd'
import { NButton, NIcon, NSelect } from 'naive-ui'
import { defineComponent, inject, onMounted, onUnmounted, ref } from 'vue'
import { useI18n } from 'vue-i18n'
import { SqlConsoleSymbol } from '..'
import useEngineConfig from '../components/use-engine-config'
import useStatus from '../components/use-status'
import CodeEditor from './code-editor'
import ExecPanel from './exec-panel'
import styles from './index.module.scss'

const props = {
  dbName: {
    type: String,
    default: ''
  },
  consoleName: {
    type: String,
    default: ''
  },
  bottomDivHeight: {
    type: Number
  },
  onChangeCurrentSqlTabBottomDiv: {
    type: Function,
    default: () => {}
  },
  order: {
    tope: Number,
    default: 0
  }
}

const QueryPanel = defineComponent({
  name: 'query-panel',
  props,
  setup(props: any, context) {
    const { t } = useI18n()

    const timerSet = inject(SqlConsoleSymbol, '')

    const initSql = ref<string>('show tables;')
    const sql = ref<string>('')
    const selectedSql = ref<string>('')
    const jobs = ref<SqlExecResult>({} as SqlExecResult)
    const execDate = ref<string>('')
    const currentExecTab = ref<string>('job_history')
    const editorRef = ref<any>({})

    const { engineTypeSet, engineType, instance, instanceOptions } =
      useEngineConfig()
    const { switchStatus } = useStatus()
    const $sqlExecTabStore = useSqlExecTabStore()

    onMounted(() => {
      const resizeDiv = document.getElementsByClassName('editor-resize') as any
      const topDiv = document.getElementsByClassName('CodeMirror') as any
      const bottomDiv = document.getElementsByClassName('footer') as any
      const fixResize = document.getElementsByClassName(
        'fix-width-resize'
      ) as any
      const editorDiv = document.getElementsByClassName('editor') as any

      for (let i = 0; i < resizeDiv.length; i++) {
        resizeDiv[i].onmousedown = function (e: { clientY: any }) {
          const startY = e.clientY
          document.onmousemove = function (e) {
            const endY = e.clientY
            // The distance between the table and the top of the browser
            const bottomDivTop = bottomDiv[i].getBoundingClientRect().y

            const offsetSize = bottomDivTop - startY - 10
            // Set the position of the fix-width-resize line
            if (
              /msie|trident/g.test(window.navigator.userAgent.toLowerCase())
            ) {
              // ie
              fixResize[i].style.top = endY + offsetSize + 'px'
            } else {
              fixResize[i].style.top = endY - 145 + offsetSize + 'px'
            }
            fixResize[i].style.width = editorDiv[i].clientWidth + 'px'
          }

          document.onmouseup = function (e) {
            const endY = e.clientY
            const move = startY - endY
            const originTopDivHeight = topDiv[i].clientHeight

            // Deviation calculation, the distance between the mouse drop and the fix-width-resize line
            let offsetSize = 0

            // The distance between the table and the top of the browser
            const bottomDivTop = bottomDiv[i].getBoundingClientRect().top

            offsetSize = bottomDivTop - startY - 12
            const windowHeight = window.innerHeight

            // set 20 is the height of the gap between the editor and the execution status bar
            const gutterHeight = 20

            // Determine if the table still has room from the bottom
            if (windowHeight - e.pageY - offsetSize > 300) {
              bottomDiv[i].style.height =
                windowHeight -
                bottomDivTop +
                move -
                gutterHeight -
                offsetSize +
                'px'

              // Set the height of the table
              props.onChangeCurrentSqlTabBottomDiv(
                windowHeight -
                  bottomDivTop +
                  move -
                  gutterHeight -
                  offsetSize -
                  135
              )

              // set editor height
              topDiv[i].style.height =
                originTopDivHeight - move + offsetSize + 'px'
              fixResize[i].style.top =
                topDiv[i].style.height.slice(
                  0,
                  topDiv[i].style.height.length - 2
                ) +
                60 +
                'px'
            } else {
              bottomDiv[i].style.height = '300px'
              props.onChangeCurrentSqlTabBottomDiv(165)
              // set editor height
              topDiv[i].style.height =
                windowHeight - 300 - 245 - gutterHeight + 'px'
              fixResize[i].style.top =
                windowHeight - 300 - 245 - gutterHeight + 60 + 'px'
            }

            document.onmousemove = null
            document.onmouseup = null
            resizeDiv[i].releaseCapture && resizeDiv[i].releaseCapture()
          }

          resizeDiv[i].setCapture && resizeDiv[i].setCapture()
          return false
        }

        window.addEventListener('resize', () => {
          const windowHeight = window.innerHeight
          // The height of the gap between the editor and the execution status bar
          const gutterHeight = 20
          bottomDiv[i].style.height = '300px'
          props.onChangeCurrentSqlTabBottomDiv(165)

          // set editor height
          topDiv[i].style.height =
            windowHeight - 300 - 245 - gutterHeight + 'px'
          fixResize[i].style.top = windowHeight - 245 - gutterHeight + 'px'
        })
      }
    })

    const onChangeText = (editorString: any) => {
      sql.value = editorString
    }

    const onSelectText = (sql: any) => {
      selectedSql.value = sql
    }

    // execute sql
    const onHandleExecSql = () => {
      // check instance and engineType not empty
      if (!instance.value || !engineType.value) {
        // TODO:
        // context.root.$Message.warning('请选择实例与执行引擎!')
        return
      }
      // During each execution, you need to clear all the data of the last execution and reopen the execution status window
      // clear current timer
      const execStatusTabs = $sqlExecTabStore.getAllExecTabs
      const activeExecTabs = execStatusTabs.filter(
        (item) =>
          item.consoleName == 'history' ||
          item.consoleName === props.consoleName
      )
      activeExecTabs.forEach((tab: SqlExec) => {
        timerSet.clearTimer(tab.jobId)
      })
      // remove exec tab store
      $sqlExecTabStore.deleteTabByConsoleName(props.consoleName)

      // set active tab to job_history tab
      currentExecTab.value = 'job_history'

      const sqlString = selectedSql.value ? selectedSql.value : sql.value

      executeSql(
        props.dbName,
        engineType.value,
        instance.value,
        sqlString
      ).then((res: SqlExecResult) => {
        jobs.value = res
        // create jobs run status tab
        jobs.value.sqlQueryJobItemDtos.sort(
          (a: SqlQueryJobItemDtos, b: SqlQueryJobItemDtos) =>
            a.jobId.localeCompare(b.jobId)
        )
        jobs.value.sqlQueryJobItemDtos.map((job: SqlQueryJobItemDtos) => {
          const status = job.message
            ? 'SUBMIT_FAILED'
            : job.result
            ? 'FINISHED'
            : 'SUBMITTED'
          const statuName = switchStatus(status)
          $sqlExecTabStore.addTab({
            jobId: job.jobId,
            label: statuName + `(${job.jobId})`,
            sql: job.sql,
            status: status,
            engineType: engineType.value,
            consoleName: props.consoleName,
            errorMessage: job.message,
            resultString: job.result,
            closable: true
          } as SqlExec)
        })
        // Set periodic build requests
        for (let i = 0; i < jobs.value.sqlQueryJobItemDtos.length; i++) {
          const jobItem = jobs.value.sqlQueryJobItemDtos[i]
          // when job executed result was failed or alreay returned result, then remove timer
          if (!jobItem.message && !jobItem.result) {
            timerSet.addTimer(jobItem.jobId, engineType.value, context)
          }
        }
        execDate.value = new Date().getTime().toString()
      })
    }

    // moveout component clear timer
    onUnmounted(() => {
      if (Object.keys(jobs.value).length > 0) {
        for (let i = 0; i < jobs.value.sqlQueryJobItemDtos.length; i++) {
          const jobId = jobs.value.sqlQueryJobItemDtos[i].jobId
          timerSet.clearTimer(jobId)
        }
      }
    })

    const onHandleFormatSql = () => {
      formatSql(props.dbName, sql.value).then((res: string) => {
        initSql.value = res.replace('\n', '<br/>')
      })
    }

    return {
      t,
      engineType,
      engineTypeSet,
      instance,
      instanceOptions,
      initSql,
      editorRef,
      execDate,
      currentExecTab,
      onChangeText,
      onSelectText,
      onHandleFormatSql,
      onHandleExecSql
    }
  },
  render() {
    return (
      <div class={styles.queryPanel}>
        <div class={styles.optionBar}>
          <div class={styles.optionButtons}>
            <span class={styles.span}>{this.t('instance.engine_type')}</span>
            <NSelect
              v-model={[this.engineType, 'value']}
              options={this.engineTypeSet}
              style={'width: 150px'}
            ></NSelect>
            <span style='margin-left: 8px;' class={styles.span}>
              {this.t('common.instance')}
            </span>
            <NSelect
              v-model={[this.instance, 'value']}
              options={this.instanceOptions}
              style={'width: 250px'}
            ></NSelect>
            <NButton
              type='primary'
              style='margin-left: 10px;'
              onClick={this.onHandleExecSql}
            >
              {this.t('common.excution')}
            </NButton>
            <NButton
              type='default'
              style='margin-left: 10px;'
              onClick={this.onHandleFormatSql}
            >
              {this.t('common.format')}
            </NButton>
          </div>
          <div class={styles.grammerButton}>
            <span>{this.t('explore.query_panel_grammar_url')}</span>
          </div>
        </div>
        <div class={styles.editor}>
          <div class={styles.editorHeader}>
            <NIcon>
              <ExclamationCircleFilled />
            </NIcon>
            {this.t('explore.sqlEditorTip')}
          </div>
          <CodeEditor
            ref='editorRef'
            sql={this.initSql}
            onChangeText={this.onChangeText}
            onSelectText={this.onSelectText}
          />
        </div>
        <div class='footer'>
          <ExecPanel
            consoleName={this.consoleName}
            dbName={this.dbName}
            execDate={this.execDate}
            currentExecTab={this.currentExecTab}
            bottomDivHeight={this.bottomDivHeight}
          />
        </div>
      </div>
    )
  }
})

export default QueryPanel
